// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... using LargoCommon.Interfaces; using System; using System.Diagnostics.Contracts; using System.Linq; using System.Text; using System.Xml.Serialization; namespace LargoCommon.Music { /// Harmonic cluster. /// /// Harmonic cluster is a set of tones representing a cross-section of voices in given time point. /// It is designed for check of tone doubling and for computation of actual Ambit, density, /// measure of dissonance, potential,... [Serializable] //// [XmlInclude(typeof(MusicalTone))] [XmlRoot] public sealed class HarmonicCluster : IHarmonic { #region Fields /// List of tones. private readonly MusicalToneCollection toneList; /// /// Harmonic system. /// private HarmonicSystem harSystem; /// /// Harmonic structure. /// private HarmonicStructure harStruct; /// List of tones. private MusicalToneCollection validToneList; //// MusicalToneCollection /// String of musical symbols. private string toneSchema; /// /// Real harmonic state of the cluster. /// private HarmonicStateReal realHarmonicState; /// /// Formal harmonic state of the cluster. /// private HarmonicStateFormal formalHarmonicState; #endregion #region Constructors /// Initializes a new instance of the HarmonicCluster class. Serializable. public HarmonicCluster() { this.FormalEnergy = new HarmonicBehavior(); this.RealEnergy = new HarmonicBehavior(); } /// Initializes a new instance of the HarmonicCluster class. /// Harmonic structure. /// Rhythmical tick. /// Rhythmical duration. public HarmonicCluster(HarmonicStructure harmonicStructure, byte tick, byte duration) : this() { this.HarmonicStructure = harmonicStructure; this.Tick = tick; this.Duration = duration; this.toneList = new MusicalToneCollection(); } #endregion #region Properties /// /// Gets or sets the bar number. /// /// The bar number. public int BarNumber { get; set; } /// Gets harmonic system. /// Property description. [XmlIgnore] public HarmonicSystem HarmonicSystem { get { Contract.Ensures(Contract.Result() != null); if (this.harSystem == null) { throw new InvalidOperationException("Harmonic system is null."); } return this.harSystem; } } /// Gets or sets harmonics structure corresponding to the cluster. /// Property description. public HarmonicStructure HarmonicStructure { get => this.harStruct; set { this.harStruct = value; if (value != null) { this.harSystem = value.HarmonicSystem; } } } /// Gets or sets first tick of the cluster. /// Property description. public byte Tick { get; set; } /// Gets or sets duration of the cluster. /// Property description. public byte Duration { get; set; } /// Gets or sets current effective length of the cluster. /// Property description. public byte CurrentEffectiveLength { get; set; } /// /// Gets the formal energy. /// /// Property description. public HarmonicBehavior FormalEnergy { get; } /// /// Gets the real energy. /// /// Property description. public HarmonicBehavior RealEnergy { get; } /// Gets or sets the property. /// Property description. public float IndexOfCentre { get; set; } /// Gets or sets the property. /// Property description. public float Weight { get; set; } /// Gets or sets the property. /// Property description. public int Ambit { get; set; } /// Gets or sets the property. /// Property description. public int FormalAmbit { get; set; } /// Gets or sets the property. /// Property description. public float Density { get; set; } /// Gets list of all tones. /// Property description. public MusicalToneCollection ToneList { get { Contract.Ensures(Contract.Result() != null); if (this.toneList == null) { throw new InvalidOperationException("List of tones is null."); } return this.toneList; } } /// Gets list of all already defined tones. /// Property description. public MusicalToneCollection ValidToneList { get { Contract.Ensures(Contract.Result() != null); if (this.validToneList == null) { throw new InvalidOperationException("Valid tone list must not be null."); } return this.validToneList; } } /// /// Gets real harmonic state of the cluster. /// /// Property description. public HarmonicStateReal RealHarmonicState { get { Contract.Ensures(Contract.Result() != null); return this.realHarmonicState ?? (this.realHarmonicState = new HarmonicStateReal(this.HarmonicSystem, this.ValidToneList)); } } /// /// Gets formal harmonic state of the cluster. /// /// Property description. public HarmonicStateFormal FormalHarmonicState { get { Contract.Ensures(Contract.Result() != null); if (this.formalHarmonicState != null) { return this.formalHarmonicState; } if (this.HarmonicStructure == null) { throw new InvalidOperationException("Harmonic structure is null."); } this.formalHarmonicState = new HarmonicStateFormal(this.HarmonicSystem, this.HarmonicStructure); return this.formalHarmonicState; } } /// Gets cluster duration. /// Property description. public HarmonicClusterExtent Extent { get; private set; } /// Gets or sets string of musical tone symbols. /// Property description. [XmlAttribute] public string ToneSchema { get => this.toneSchema ?? (this.toneSchema = this.ToneList.ToneSchema); set => this.toneSchema = value; } /// /// Gets a value indicating whether Is chord. /// /// Property description. public bool IsChord => this.IsValid && this.ValidToneList.Count > 1 && this.harSystem != null; /// /// Returns true if ... is valid. /// /// /// true if this instance is valid; otherwise, false. /// public bool IsValid => this.validToneList != null; #endregion #region Cluster tones /// Adds tone to the cluster. /// Melodic tone. public void AddMelodicTone(MusicalTone givenTone) { this.ToneList.Add(givenTone); } /// Returns all the harmonic tones. /// Returns value. public MusicalToneCollection HarmonicTones() { if (this.HarmonicStructure == null) { return null; } var hs = this.HarmonicStructure; var tones = from mt in this.ValidToneList where hs.IsOn(mt.Pitch.Element) select mt; var collection = new MusicalToneCollection(tones, false); return collection; //// new MusicalToneCollection(tones.ToList()); } /// Returns all the non-harmonic tones. /// Returns value. public MusicalToneCollection MelodicTones() { if (this.HarmonicStructure == null) { return null; } var hs = this.HarmonicStructure; var tones = from mt in this.ValidToneList where hs.IsOff(mt.Pitch.Element) select mt; var collection = new MusicalToneCollection(tones, false); return collection; //// new MusicalToneCollection(list.ToList()); } /// Returns number of tones corresponding to given formal element. /// Pitch element. /// Returns value. public int NumberOfEqualTones(byte givenPitchElement) { var number = (from melTone in this.ToneList where melTone.IsTrueTone && (melTone.Pitch.Element == givenPitchElement) select 1) .Count(); return number; } /// /// Numbers the of mel tones. /// /// Returns value. public int NumberOfTrueTones() { var number = (from melTone in this.ToneList where melTone.IsTrueTone select 1) .Count(); return number; } #endregion #region Public methods /// Completes initialization (obsolete). public void Recompute() { //// Properties.Clear(); if ((this.ToneList == null) || (this.ToneList.Count <= 0)) { return; } ////SortToneArray(); this.validToneList = this.ToneList.ValidToneList; this.realHarmonicState = null; //// ?!? this.formalHarmonicState = null; if (this.validToneList.Count > 0) { //// // // list.Sort(); this.ComputeAmbit(); //// this.validToneList this.ComputeIndexOfCentre(); //// this.validToneList this.ComputeState(); //// this.validToneList this.ComputeWeight(); //// this.validToneList } this.toneSchema = null; } /// Returns formal representation of the cluster. /// Returns value. public HarmonicStructure FormalStruct() { this.ToneList.Where(mt => mt != null && mt.IsTrueTone).Aggregate(0L, (current, mt) => current | BinaryNumber.BitAt(mt.Pitch.Element)); var hS = HarmonicStructure.GetNewHarmonicStructure(this.HarmonicSystem, null); //// number return hS; } #endregion #region String representation /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(); //// s.AppendFormat("Harmonic Cluster"); //// s.Append(this.StringOfProperties()+"\n"); //// s.Append(harStruct.ToString(CultureInfo.CurrentCulture)); foreach (var mt in this.ToneList.Where(mt => mt != null)) { s.Append(mt.ToShortString() + ","); } //// s.Append(StringOfProperties()); return s.ToString(); } #endregion #region Computation of properties /// Sets properties derived from Ambit. public void ComputeState() { ///// MusicalToneCollection melodicTones //// Contract.Requires(melodicTones != null); //// if (melodicTones == null) { return; } if (this.HarmonicStructure == null) { return; } //// HarmonicSystem hS = (HarmonicSystem)this.HarmonicStructure.HarmonicSystem; if (this.ValidToneList.Count != 0) { //// HarmonicStateReal state = new HarmonicStateReal(hS, melodicTones); //// string key = this.ValidToneList.ToneKey; var state = this.RealHarmonicState; //// if (HarmonicStateReal.UsedStates.ContainsKey(key)) { state = HarmonicStateReal.UsedStates[key]; //// } else { state = this.RealHarmonicState; HarmonicStateReal.UsedStates[key] = state; } this.RealEnergy.Continuity = state.RealContinuity; //// GetProperty(GenProperty.RealContinuity); this.RealEnergy.Impulse = state.RealImpulse; //// GetProperty(GenProperty.RealImpulse); this.RealEnergy.Potential = state.RealPotential; //// GetProperty(GenProperty.RealPotential); this.RealEnergy.Consonance = state.RealConsonance; //// GetProperty(GenProperty.RealConsonance); } else { this.RealEnergy.Consonance = 100f; } //// HarmonicStateFormal formalState = new HarmonicStateFormal(hS, HarmonicStructure); //// 2019/05 if (this.HarmonicStructure.Level > 0) { var formalState = this.FormalHarmonicState; this.FormalEnergy.Continuity = formalState.FormalContinuity; //// GetProperty(GenProperty.FormalContinuity); this.FormalEnergy.Impulse = formalState.FormalImpulse; //// GetProperty(GenProperty.FormalImpulse); this.FormalEnergy.Potential = formalState.FormalPotential; //// GetProperty(GenProperty.FormalPotential); this.FormalEnergy.Consonance = formalState.FormalConsonance; //// GetProperty(GenProperty.FormalConsonance); this.FormalEnergy.Balance = 0; } } #endregion #region Private methods /// Determines estimated cluster extent. /// Density of cluster. private void DetermineExtent(float givenDensity) { const float wideToMiddleBreak = 6.0f; const float middleToTightBreak = 3.0f; this.Extent = givenDensity > middleToTightBreak ? givenDensity > wideToMiddleBreak ? HarmonicClusterExtent.WideExtent : HarmonicClusterExtent.MiddleExtent : HarmonicClusterExtent.TightExtent; } /// Compute pitch center of the cluster. private void ComputeIndexOfCentre() { //// Contract.Requires(melodicTones != null); var melodicTones = this.validToneList; if (melodicTones == null || melodicTones.Count == 0) { return; } float centralIndex; if (melodicTones.Count > 1) { float total = (from mt in melodicTones select mt.Pitch.SystemAltitude).Sum(); //// if (melodicTones.Count != 0) { centralIndex = total / melodicTones.Count; //// } } else { centralIndex = melodicTones.First().Pitch.SystemAltitude; } this.IndexOfCentre = centralIndex; } /// Sets properties derived from Ambit. private void ComputeWeight() { //// Contract.Requires(melodicTones != null); var melodicTones = this.validToneList; if (melodicTones == null || melodicTones.Count == 0) { return; } float weight = 0; if (melodicTones.Count > 1) { var total = (from mt in melodicTones select mt.Weight).Sum(); if (this.ToneList.Count > 0) { weight = total / this.ToneList.Count; } } else { weight = 1.0f; } this.Weight = weight; } /// Sets properties derived from Ambit. private void ComputeAmbit() { //// Contract.Requires(melodicTones != null); var melodicTones = this.validToneList; if (melodicTones == null || melodicTones.Count == 0) { return; } var sysLength = 0; if (melodicTones.Count > 1) { //// melodicTones.Count != 0 var t0 = melodicTones.ElementAt(0); var t1 = melodicTones.ElementAt(melodicTones.Count - 1); sysLength = t0 != null && t1 != null && t0.IsTrueTone && t1.IsTrueTone ? t1.Pitch.SystemAltitude - t0.Pitch.SystemAltitude : 0; } float realAmbit = sysLength; var formalAmbit = this.HarmonicStructure?.HarmonicSystem.FormalLength(sysLength) ?? 0; var density = realAmbit / melodicTones.Count; //// melodicTones.Count > 1 ? realAmbit / melodicTones.Count : 0; this.Ambit = (int)realAmbit; this.FormalAmbit = formalAmbit; this.Density = density; this.DetermineExtent(density); } #endregion } }